package api

import (
	"context"
	"encoding/json"
	"github.com/ethereum/go-ethereum"
	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/ethclient"
	"github.com/gin-gonic/gin"
	"go-chain-api/contract"
	"go-chain-api/contract/evmAbi"
	"go-chain-api/global"
	models "go-chain-api/internal/model"
	"go-chain-api/tools"
	"go-chain-api/utils"
	"log"
	"math"
	"math/big"
	"net/http"
	"strconv"
	"strings"
)

type Block struct {
}

type BlockInfo struct {
	BlockHeight       uint64 `json:"block_height"`
	BlockHash         string `json:"block_hash"`
	ParentHash        string `json:"parent_hash"`
	LatestBlockHeight uint64 `json:"latest_block_height"`
	Timestamp         uint64 `json:"timestamp"`
	BlockReward       string `json:"block_reward"`
	Difficulty        string `json:"difficulty"`
	Size              uint64 `json:"size"`
	GasUsed           uint64 `json:"gas_used"`
	GasLimit          uint64 `json:"gas_limit"`
	TransactionsCount int    `json:"transactions_count"`
	ExtraData         []byte `json:"extra_data"`
}

func (v1 Block) GetLastBlocks(c *gin.Context) {
	var response global.Response

	chainName := c.MustGet("chainName").(string)
	chainId := c.MustGet("chainId").(uint)
	blocks := models.GetLatest20(chainName, chainId)
	response = global.Response{
		Code: http.StatusOK,
		Data: blocks,
		Msg:  "success",
	}
	c.JSON(http.StatusOK, response)
}

// GetTokensRecords 获取代币转账记录
func (v1 Block) GetTokensRecords(c *gin.Context) {
	var response global.Response
	address := c.DefaultQuery("address", "")
	pageIndex := c.MustGet("pageIndex").(uint)
	pageSize := c.MustGet("pageSize").(uint)
	chainName := c.MustGet("chainName").(string)
	chainId := c.MustGet("chainId").(uint)
	address = common.HexToHash(address).String()

	// 查询总条数
	count := models.GetDataByAddressCount(chainName, chainId, address)
	if int(count) == 0 {
		response = global.Response{
			Code: http.StatusOK,
			Data: global.Page{
				Total: 0,
				List:  []string{},
			},
			Msg: "success",
		}
		c.JSON(http.StatusOK, response)
		return
	}

	lists := models.GetTokenRecordByAddress(chainName, chainId, address, pageIndex, pageSize)
	response = global.Response{
		Code: http.StatusOK,
		Data: global.Page{
			Total: count,
			List:  lists,
		},
		Msg: "success",
	}
	c.JSON(http.StatusOK, response)
}

// GetBlockByPage 获取区块列表
func (v1 Block) GetBlockByPage(c *gin.Context) {
	var response global.Response
	pageIndex := c.MustGet("pageIndex").(uint)
	pageSize := c.MustGet("pageSize").(uint)
	chainName := c.MustGet("chainName").(string)
	chainId := c.MustGet("chainId").(uint)
	blocks, _ := models.GetBlockInfoByName(chainName, chainId)

	client, err := ethclient.Dial(blocks.RpcUrl)
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()
	// 获取最新区块
	endBlockNumber, _ := client.BlockNumber(c)
	//if pageIndex == 0 {
	//        pageIndex = 1
	//}
	// 根据分页推算需要获取的区块
	endBlockNumber = endBlockNumber - uint64(pageIndex)*uint64(pageSize)
	startBlockNumber := endBlockNumber - uint64(pageSize)

	var blockInfos []BlockInfo
	// 获取区块
	for i := endBlockNumber; i >= startBlockNumber; i-- {
		block, err := client.BlockByNumber(c, big.NewInt(int64(i)))
		if err != nil {
			log.Fatal(err)
		}
		blockInfos = append(blockInfos, BlockInfo{
			BlockHeight:       block.NumberU64(),
			BlockHash:         block.Hash().String(),
			ParentHash:        block.ParentHash().String(),
			LatestBlockHeight: endBlockNumber,
			Timestamp:         block.Time(),
			BlockReward:       block.BaseFee().String(),
			Difficulty:        block.Difficulty().String(),
			Size:              block.Size(),
			GasUsed:           block.GasUsed(),
			GasLimit:          block.GasLimit(),
			TransactionsCount: len(block.Transactions()),
		})
	}

	response = global.Response{
		Code: http.StatusOK,
		Data: global.Page{
			Total: int64(endBlockNumber),
			List:  blockInfos,
		},
		Msg: "success",
	}

	c.JSON(http.StatusOK, response)
}

func (v1 Block) GetBlockByNumber(c *gin.Context) {
	var response global.Response
	blockNumber := c.DefaultQuery("blockNumber", "1")
	chainName := c.MustGet("chainName").(string)
	chainId := c.MustGet("chainId").(uint)
	blocks, _ := models.GetBlockInfoByName(chainName, chainId)

	client, err := ethclient.Dial(blocks.RpcUrl)
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	blockNumberInt, _ := new(big.Int).SetString(blockNumber, 10)
	block, err := client.BlockByNumber(c, blockNumberInt)
	if err != nil {
		log.Fatal(err)
	}
	response = global.Response{
		Code: http.StatusOK,
		Data: BlockInfo{
			BlockHeight:       block.NumberU64(),
			BlockHash:         block.Hash().String(),
			ParentHash:        block.ParentHash().String(),
			LatestBlockHeight: blockNumberInt.Uint64(),
			Timestamp:         block.Time(),
			BlockReward:       block.BaseFee().String(),
			Difficulty:        block.Difficulty().String(),
			Size:              block.Size(),
			GasUsed:           block.GasUsed(),
			GasLimit:          block.GasLimit(),
			TransactionsCount: len(block.Transactions()),
		},
		Msg: "success",
	}

	c.JSON(http.StatusOK, response)
}

func (v1 Block) GetTxRecords(c *gin.Context) {
	var response global.Response
	pageIndex := c.MustGet("pageIndex").(uint)
	pageSize := c.MustGet("pageSize").(uint)
	chainName := c.MustGet("chainName").(string)

	count, last20, err := models.GetTransactionRecords(chainName, pageIndex, pageSize)
	if err != nil {
		response = global.Response{
			Code: http.StatusInternalServerError,
			Data: nil,
			Msg:  "get tx fail",
		}
		c.JSON(http.StatusInternalServerError, response)
		return
	}
	// 查询总条数
	response = global.Response{
		Code: http.StatusOK,
		Data: global.Page{
			Total: count,
			List:  last20,
		},
		Msg: "success",
	}
	c.JSON(http.StatusOK, response)
}

func (v1 Block) GetTxByHash(c *gin.Context) {
	var response global.Response
	txHash := c.Query("txHash")

	// 参数校验
	if txHash == "" {
		response = global.Response{
			Code: http.StatusBadRequest,
			Data: nil,
			Msg:  "txHash is required",
		}
		c.JSON(http.StatusBadRequest, response)
		return
	}

	hash, err := models.GetTransactionByHash(c.MustGet("chainName").(string), txHash)
	if err != nil {
		response = global.Response{
			Code: http.StatusInternalServerError,
			Data: nil,
			Msg:  "get tx fail",
		}
		c.JSON(http.StatusInternalServerError, response)
		return
	}

	response = global.Response{
		Code: http.StatusOK,
		Data: hash,
		Msg:  "success",
	}

	c.JSON(http.StatusOK, response)
}

func (v1 Block) GetTxByAddress(c *gin.Context) {
	var response global.Response
	address := c.DefaultQuery("address", "")
	pageIndex := c.MustGet("pageIndex").(uint)
	pageSize := c.MustGet("pageSize").(uint)
	chainName := c.MustGet("chainName").(string)
	chainId := c.MustGet("chainId").(uint)

	byAddress, list, err := models.GetTransactionByAddress(chainName, chainId, address, pageIndex, pageSize)
	if err != nil {
		response = global.Response{
			Code: http.StatusInternalServerError,
			Data: nil,
			Msg:  "get tx fail",
		}
		c.JSON(http.StatusInternalServerError, response)
		return
	}

	response = global.Response{
		Code: http.StatusOK,
		Data: global.Page{
			Total: byAddress,
			List:  list,
		},
		Msg: "success",
	}

	c.JSON(http.StatusOK, response)
}

// GetTokenHolders 获取token持有者
func (v1 Block) GetTokenHolders(c *gin.Context) {
	var response global.Response
	token := c.DefaultQuery("token", "")
	chainName := c.MustGet("chainName").(string)
	chainId := c.MustGet("chainId").(uint)
	count, err := models.GetTokenHoldersCount(chainName, chainId, token)
	if err != nil {
		response = global.Response{
			Code: http.StatusInternalServerError,
			Data: nil,
			Msg:  err.Error(),
		}
		c.JSON(http.StatusInternalServerError, response)
		return
	}
	response = global.Response{
		Code: http.StatusOK,
		Data: count,
		Msg:  "success",
	}
	c.JSON(http.StatusOK, response)
}

// GetTokenTransferCount 获取token转账总数
func (v1 Block) GetTokenTransferCount(c *gin.Context) {
	var response global.Response
	token := c.DefaultQuery("token", "")
	chainName := c.MustGet("chainName").(string)

	count, err := models.GetTokenTxCount(chainName, token)
	if err != nil {
		response = global.Response{
			Code: http.StatusInternalServerError,
			Data: nil,
			Msg:  err.Error(),
		}
		c.JSON(http.StatusInternalServerError, response)
		return
	}

	response = global.Response{
		Code: http.StatusOK,
		Data: count,
		Msg:  "success",
	}

	c.JSON(http.StatusOK, response)
}

// GetTokenTransferAmount 获取token转账总额
func (v1 Block) GetTokenTransferAmount(c *gin.Context) {
	var response global.Response
	token := c.DefaultQuery("token", "")
	chainName := c.MustGet("chainName").(string)

	amount, err := models.GetTokenTransferAmount(chainName, token)
	if err != nil {
		response = global.Response{
			Code: http.StatusInternalServerError,
			Data: nil,
			Msg:  err.Error(),
		}
		c.JSON(http.StatusInternalServerError, response)
		return
	}

	response = global.Response{
		Code: http.StatusOK,
		Data: amount,
		Msg:  "success",
	}

	c.JSON(http.StatusOK, response)
}

// GetMinerFee 获取最少的矿工费
func (v1 Block) GetMinerFee(c *gin.Context) {
	var response global.Response
	chainName := c.MustGet("chainName").(string)
	fee, err := models.GetMinerFee(chainName)
	if err != nil {
		response = global.Response{
			Code: http.StatusInternalServerError,
			Data: nil,
			Msg:  "get miner fee fail",
		}
		c.JSON(http.StatusInternalServerError, response)
		return
	}
	response = global.Response{
		Code: http.StatusOK,
		Data: fee,
		Msg:  "success",
	}
	c.JSON(http.StatusOK, response)
}

// GetFeeTotal 获取手续费总额
func (v1 Block) GetFeeTotal(c *gin.Context) {
	var response global.Response
	chainName := c.MustGet("chainName").(string)
	fee, err := models.GetFeeTotal(chainName)
	if err != nil {
		response = global.Response{
			Code: http.StatusInternalServerError,
			Data: nil,
			Msg:  "get miner fee fail",
		}
		c.JSON(http.StatusInternalServerError, response)
		return
	}
	response = global.Response{
		Code: http.StatusOK,
		Data: fee,
		Msg:  "success",
	}
	c.JSON(http.StatusOK, response)
}

// GetTokenList 获取代币列表
func (v1 Block) GetTokenList(c *gin.Context) {
	var response global.Response
	chainName := c.MustGet("chainName").(string)
	chainId := c.MustGet("chainId").(uint)
	token := &models.TokenInfo{}
	tokens, err := token.GetTokenList(chainName, chainId)
	if err != nil {
		response = global.Response{
			Code: http.StatusInternalServerError,
			Data: nil,
			Msg:  "get tokens fail",
		}
		c.JSON(http.StatusInternalServerError, response)
		return
	}

	response = global.Response{
		Code: http.StatusOK,
		Data: tokens,
		Msg:  "success",
	}
	c.JSON(http.StatusOK, response)
}

// GetBalance 获取余额
func (v1 Block) GetBalance(c *gin.Context) {
	var response global.Response
	chainName := c.MustGet("chainName").(string)
	chainId := c.MustGet("chainId").(uint)
	address := c.Query("address")
	if address == "" {
		response = global.Response{
			Code: http.StatusInternalServerError,
			Data: nil,
			Msg:  "address is empty",
		}
		c.JSON(http.StatusInternalServerError, response)
		return

	}

	//address = common.HexToAddress(address).Hex()
	// 获取rpc地址
	blockInfo, err := models.GetBlockInfoByName(chainName, chainId)
	if err != nil {
		response = global.Response{
			Code: http.StatusInternalServerError,
			Data: nil,
			Msg:  err.Error(),
		}
		c.JSON(http.StatusInternalServerError, response)
		return
	}

	// 获取代币列表
	token := &models.TokenInfo{}
	tokens, err := token.GetTokenList(chainName, chainId)
	if err != nil {
		response = global.Response{
			Code: http.StatusInternalServerError,
			Data: nil,
			Msg:  err.Error(),
		}
		c.JSON(http.StatusInternalServerError, response)
		return
	}
	// 创建结构体
	type BalanceInfo struct {
		Name    string `json:"name"`
		Balance string `json:"balance"`
		Token   string `json:"token"`
	}

	var balanceList []BalanceInfo

	// 通过rpc地址获取公链币余额
	client, err := ethclient.Dial(blockInfo.RpcUrl)
	if err != nil {
		response = global.Response{
			Code: http.StatusInternalServerError,
			Data: nil,
			Msg:  err.Error(),
		}
		c.JSON(http.StatusInternalServerError, response)
		return
	}
	defer client.Close()

	// 查询账户余额
	balance, err := client.BalanceAt(c, common.HexToAddress(address), nil)
	if err != nil {
		response = global.Response{
			Code: http.StatusInternalServerError,
			Data: nil,
			Msg:  err.Error(),
		}
		c.JSON(http.StatusInternalServerError, response)
		return
	}

	// 将余额从wei转换为以太币
	ethBalance := new(big.Float).Quo(new(big.Float).SetInt(balance), big.NewFloat(1e18))
	ethBalanceStr := ethBalance.Text('f', 9)
	balanceList = append(balanceList, BalanceInfo{
		Name:    blockInfo.ChainName,
		Balance: ethBalanceStr,
		Token:   "",
	})

	for _, token := range tokens {

		// 代币合约ABI
		parsedABI, err := abi.JSON(strings.NewReader(utils.ERC20_ABI))
		if err != nil {
			log.Println("parsedABI_err:", err)
			break
		}

		contractAddress := common.HexToAddress(token.Address)
		data, err := parsedABI.Pack("balanceOf", common.HexToAddress(address))
		if err != nil {
			log.Println("contractAddress_err:", err)
			break
		}
		// 调用balanceOf方法
		callResult, err := client.CallContract(c, ethereum.CallMsg{
			To:   &contractAddress,
			Data: data,
			From: common.HexToAddress(address),
		}, nil)
		if err != nil {
			log.Println("callResult_err:", err)
			break
		}

		// 解析结果
		balance, err := parsedABI.Unpack("balanceOf", callResult)
		if err != nil {
			log.Println("balance_err", err)
			break
		}

		tokenBalance := new(big.Float).Quo(new(big.Float).SetInt(balance[0].(*big.Int)), big.NewFloat(math.Pow10(int(token.Decimals))))
		balanceStr := tokenBalance.Text('f', int(token.Decimals)) // 将结果格式化为固定点数表示，小数点后保留的位数与代币的小数位数相同
		balanceList = append(balanceList, BalanceInfo{
			Name:    token.Symbol,
			Balance: balanceStr,
			Token:   token.Address,
		})
	}

	response = global.Response{
		Code: http.StatusOK,
		Data: balanceList,
		Msg:  "success",
	}

	c.JSON(http.StatusOK, response)
}

// 判断地址是否是合约地址
func (v1 Block) IsContractAddress(c *gin.Context) {
	response := global.Response{
		Code: http.StatusOK,
		Data: true,
		Msg:  "success",
	}
	chainName := c.MustGet("chainName").(string)
	chainId := c.MustGet("chainId").(uint)
	address := c.Query("address")
	blocks, _ := models.GetBlockInfoByName(chainName, chainId)
	boolean, err := models.IsContractAddress(blocks.RpcUrl, address)
	if err != nil {
		response.Msg = err.Error()
		response.Code = http.StatusBadRequest
	}
	response.Data = boolean
	c.JSON(http.StatusOK, response)
}

type createRequest struct {
	ChainConfigId0 string `bson:"chainConfigId0" json:"chainConfigId0"`
	ChainConfigId1 string `bson:"chainConfigId1" json:"chainConfigId1"`
	TokenId0       string `bson:"tokenId0" json:"tokenId0"`
	TokenId1       string `bson:"tokenId1" json:"tokenId1"`
	TxHash         string `bson:"txHash" json:"txHash"`
	From           string `bson:"from" json:"from"`
	To             string `bson:"to" json:"to"`
	UserId         string `bson:"userId" json:"userId"`
	Amount0        string `bson:"amount0" json:"amount0"`
	Amount1        string `bson:"amount1" json:"amount1"`
}
type listRequest struct {
	PageIndex uint   `form:"pageIndex"` // 第几页
	PageSize  uint   `form:"pageSize"`  // 每页显示条数
	UserId    string `form:"userId"`
	From      string `form:"from"`
}

type listResponse struct {
	Code int    `json:"code"`
	Data Page   `json:"data"`
	Msg  string `json:"msg"`
}

type Page struct {
	Total int64       `json:"total"`
	List  interface{} `json:"list"`
}
type RequestParam struct {
	ChainConfigId string `form:"chainConfigId"` // 第几页
}
type QueryData struct {
	UserId         string `form:"userId"`
	ChainConfigId0 string `form:"chainConfigId0"`
	ChainConfigId1 string `form:"chainConfigId1"`
	Token0         string `form:"token0"`
	Token1         string `form:"token1"`
}

func (v1 Block) Create(c *gin.Context) {
	var response global.Response
	req := new(createRequest)
	//res := new(createResponse)
	language := c.MustGet("language").(string)

	if err := c.ShouldBindJSON(req); err != nil {
		//response = global.Response{
		//	Code: http.StatusOK,
		//	Data: utils.ParamBindError,
		//	Msg:  string(utils.ParamBindError),
		//}
		response := utils.GetResponse(err, utils.ExchangeError, nil, language)

		c.JSON(http.StatusOK, response)
		return
	}

	var createData models.ChainApply
	createData.UserId = req.UserId
	createData.To = req.To
	createData.From = req.From
	createData.TxHash = req.TxHash
	createData.TokenId1 = req.TokenId1
	createData.TokenId0 = req.TokenId0
	createData.ChainConfigId1 = req.ChainConfigId1
	createData.ChainConfigId0 = req.ChainConfigId0
	createData.Amount0 = req.Amount0
	createData.Amount1 = req.Amount1

	err := createData.Create(createData)
	if err != nil {
		//response = global.Response{
		//	Code: http.StatusOK,
		//	Data: utils.ExchangeError,
		//	Msg:  string(utils.ExchangeError),
		//}
		response := utils.GetResponse(err, utils.ExchangeError, nil, language)

		c.JSON(http.StatusOK, response)
		return
	}
	response.Code = http.StatusOK
	response.Data = nil
	response.Msg = http.StatusText(http.StatusOK)
	c.JSON(http.StatusOK, response)
}

func (v1 Block) List(c *gin.Context) {
	//var response global.Response
	req := new(listRequest)
	res := new(listResponse)
	language := c.MustGet("language").(string)
	if err := c.ShouldBindQuery(req); err != nil {
		//response = global.Response{
		//	Code: http.StatusOK,
		//	Data: utils.ParamBindError,
		//	Msg:  string(utils.ParamBindError),
		//}
		response := utils.GetResponse(err, utils.ParamBindError, nil, language)

		c.JSON(http.StatusOK, response)
		return
	}
	var createData models.ChainApply
	total, productList, err := createData.List(req.UserId, req.From, req.PageIndex, req.PageSize)
	if err != nil {
		//response = global.Response{
		//	Code: http.StatusOK,
		//	Data: utils.GetProductListError,
		//	Msg:  string(utils.GetProductListError),
		//}
		response := utils.GetResponse(err, utils.GetProductListError, nil, language)

		c.JSON(http.StatusOK, response)
		return
	}

	res.Code = http.StatusOK
	res.Data = Page{Total: total, List: productList}
	res.Msg = http.StatusText(http.StatusOK)
	//ctx.Payload(res)
	c.JSON(http.StatusOK, res)

}

func (v1 Block) TokenList(c *gin.Context) {

	var response global.Response
	req := new(RequestParam)
	//res := new(createResponse)
	language := c.MustGet("language").(string)

	if err := c.ShouldBindQuery(&req); err != nil {
		//response = global.Response{
		//	Code: http.StatusOK,
		//	Data: utils.ParamBindError,
		//	Msg:  http.StatusText(http.StatusOK),
		//}
		response := utils.GetResponse(err, utils.ParamBindError, nil, language)

		c.JSON(http.StatusOK, response)
		return
	}

	//data := new(chain_token.RequestData)
	data := req.ChainConfigId
	var tokenData models.ChainToken

	productList, err := tokenData.List(data)
	if err != nil {
		//response = global.Response{
		//	Code: http.StatusOK,
		//	Data: utils.GetProductListError,
		//	Msg:  string(utils.GetProductListError),
		//}
		response := utils.GetResponse(err, utils.GetProductListError, nil, language)

		c.JSON(http.StatusOK, response)
		return
	}
	response.Code = http.StatusOK
	response.Data = productList
	response.Msg = http.StatusText(http.StatusOK)
	//ctx.Payload(res)
	c.JSON(http.StatusOK, response)

}

func (v1 Block) ApiList(c *gin.Context) {

	req := new(QueryData)
	var response global.Response
	language := c.MustGet("language").(string)

	//res := new(createResponse)
	if err := c.ShouldBindQuery(&req); err != nil {
		//response = global.Response{
		//	Code: http.StatusOK,
		//	Data: utils.ParamBindError,
		//	Msg:  string(utils.ParamBindError),
		//}
		response := utils.GetResponse(err, utils.ParamBindError, nil, language)

		c.JSON(http.StatusOK, response)
		return
	}
	var configData models.ChainConfig
	productList, err := configData.ApiList(req.UserId)
	if err != nil {
		//response = global.Response{
		//	Code: http.StatusOK,
		//	Data: utils.GetProductListError,
		//	Msg:  string(utils.GetProductListError),
		//}
		response := utils.GetResponse(err, utils.GetProductListError, nil, language)

		c.JSON(http.StatusOK, response)
		return
	}

	response.Code = http.StatusOK
	response.Data = productList
	response.Msg = http.StatusText(http.StatusOK)
	//ctx.Payload(res)
	c.JSON(http.StatusOK, response)

}

func (v1 Block) GetRate(c *gin.Context) {

	req := new(QueryData)
	//res := new(createResponse)
	var response global.Response
	language := c.MustGet("language").(string)

	if err := c.ShouldBindQuery(&req); err != nil {
		//response = global.Response{
		//	Code: http.StatusOK,
		//	Data: utils.ParamBindError,
		//	Msg:  string(utils.ParamBindError),
		//}
		response := utils.GetResponse(err, utils.ParamBindError, nil, language)
		c.JSON(http.StatusOK, response)
		return
	}

	data := new(models.CreateChainData)
	data.Token1 = req.Token1
	data.Token0 = req.Token0
	data.ChainConfigId0 = req.ChainConfigId0
	data.ChainConfigId1 = req.ChainConfigId1
	data.UserId = req.UserId

	var ChainData models.ChainToChain
	productList, err := ChainData.GetRate(data)
	if err != nil {
		//response = global.Response{
		//	Code: http.StatusOK,
		//	Data: utils.GetProductListError,
		//	Msg:  string(utils.GetProductListError),
		//}
		//utils.GetResponse(err, http.StatusOK, response, "")
		response := utils.GetResponse(err, utils.GetProductListError, nil, language)

		c.JSON(http.StatusOK, response)
		return
	}
	response.Code = http.StatusOK
	response.Data = productList.Rate.String()
	response.Msg = http.StatusText(http.StatusOK)
	//ctx.Payload(res)
	c.JSON(http.StatusOK, response)
}

func (v1 Block) GetTokenInfo(c *gin.Context) {
	var response global.Response
	address := c.Query("address")
	var token models.TokenInfo
	tokenInfo := token.GetTokenInfoByAddress(address)

	response.Code = http.StatusOK
	response.Data = tokenInfo
	response.Msg = http.StatusText(http.StatusOK)
	//ctx.Payload(res)
	c.JSON(http.StatusOK, response)
}

type TxData struct {
	From         string `json:"from"`
	To           string `json:"to"`
	TxHash       string `json:"hash"`
	Value        string `json:"value"`
	TokenValue   string `json:"tokenValue"`
	TokenName    string `json:"tokenName"`
	GasFee       string `json:"gasFee"`
	Timestamp    uint64 `json:"timestamp"`
	Status       uint64 `json:"status"`
	TokenAddress string `json:"tokenAddress"`
}

func (v1 Block) GetRecordByAddress(c *gin.Context) {
	var response global.Response
	address := c.DefaultQuery("address", "")
	pageIndex := c.MustGet("pageIndex").(uint)
	pageSize := c.MustGet("pageSize").(uint)
	chainName := c.MustGet("chainName").(string)
	chainId := c.MustGet("chainId").(uint)
	tokenAddress := c.Query("tokenAddress")

	if address == "" {
		response = global.Response{
			Code: http.StatusInternalServerError,
			Data: nil,
			Msg:  "address is empty",
		}
		c.JSON(http.StatusInternalServerError, response)
		return
	}
	// 查询链信息
	configData, err := models.GetConfigData(chainName, chainId, "")
	if err != nil {
		response = global.Response{
			Code: http.StatusInternalServerError,
			Data: nil,
			Msg:  err.Error(),
		}
		c.JSON(http.StatusInternalServerError, response)
		return
	}

	// 查询代币详情
	token := &models.TokenInfo{}
	tokenInfo := &models.TokenInfo{}

	// 主币交易
	if len(tokenAddress) < 10 {
		// 查询交易记录
		dataList, err := models.GetMainRecordByAddress(configData.ChainName, uint(configData.ChainID), address, pageIndex, pageSize)
		if err != nil {
			c.JSON(http.StatusInternalServerError, global.Response{
				Code: http.StatusInternalServerError,
				Data: nil,
				Msg:  err.Error(),
			})
			return
		}

		c.JSON(http.StatusOK, global.Response{
			Code: http.StatusOK,
			Data: dataList,
			Msg:  "",
		})
		return
	} else {
		tokenInfo = token.GetTokenInfoByAddress(tokenAddress)
	}
	if tokenInfo.Address == "" {
		// 数据库中没有查询到 调用合约查询
		client, err := ethclient.Dial(configData.RpcUrl)
		if err != nil {
			log.Println(err)
		}
		tokenName, err := contract.CallContractMethod(client, common.HexToAddress(tokenAddress), evmAbi.ERC20_ABI, "name")
		if err != nil {
			c.JSON(http.StatusInternalServerError, global.Response{
				Code: http.StatusInternalServerError,
				Data: nil,
				Msg:  err.Error(),
			})
			return
		}
		tokenSym, err := contract.CallContractMethod(client, common.HexToAddress(tokenAddress), evmAbi.ERC20_ABI, "symbol")
		if err != nil {
			c.JSON(http.StatusInternalServerError, global.Response{
				Code: http.StatusInternalServerError,
				Data: nil,
				Msg:  err.Error(),
			})
		}
		tokenDec, err := contract.CallContractMethod(client, common.HexToAddress(tokenAddress), evmAbi.ERC20_ABI, "decimals")
		if err != nil {
			c.JSON(http.StatusInternalServerError, global.Response{
				Code: http.StatusInternalServerError,
				Data: nil,
				Msg:  err.Error(),
			})
			return
		}
		tokenTot, err := contract.CallContractMethod(client, common.HexToAddress(tokenAddress), evmAbi.ERC20_ABI, "totalSupply")
		if err != nil {
			c.JSON(http.StatusInternalServerError, global.Response{
				Code: http.StatusInternalServerError,
				Data: nil,
				Msg:  err.Error(),
			})
			return
		}

		// 插入到token表中
		tokenInfo.Name = tokenName[0].(string)
		tokenInfo.Symbol = tokenSym[0].(string)
		tokenInfo.Decimals = uint(tokenDec[0].(uint8))
		tokenInfo.Address = common.HexToAddress(tokenAddress).Hex()
		tokenInfo.ChainName = configData.ChainName
		tokenInfo.ChainID = configData.ChainID
		tokenInfo.TotalSupply = tokenTot[0].(*big.Int).String()
		err = tokenInfo.AddToken()
		if err != nil {
			c.JSON(http.StatusInternalServerError, global.Response{
				Code: http.StatusInternalServerError,
				Data: nil,
				Msg:  err.Error(),
			})
			return
		}
	}

	// 查询交易记录
	dataList, err := models.GetRecordByAddressAndContract(configData.ChainName, uint(configData.ChainID), address, tokenAddress, pageIndex, pageSize, tokenInfo.Decimals)
	if err != nil {
		c.JSON(http.StatusInternalServerError, global.Response{
			Code: http.StatusInternalServerError,
			Data: nil,
			Msg:  err.Error(),
		})
		return
	}

	c.JSON(http.StatusOK, global.Response{
		Code: http.StatusOK,
		Data: dataList,
		Msg:  "",
	})

}

type Response struct {
	Result []struct {
		DisplayData struct {
			ResultData struct {
				TplData struct {
					Result struct {
						MinuteData struct {
							Cur struct {
								Price string `json:"price"`
							} `json:"cur"`
						} `json:"minute_data"`
					} `json:"result"`
				} `json:"tplData"`
			} `json:"resultData"`
		} `json:"DisplayData"`
	} `json:"Result"`
}

func (v1 Block) GetPrice(c *gin.Context) {

	var response global.Response
	chainName := c.MustGet("chainName").(string)
	chainId := c.MustGet("chainId").(uint)
	address := c.Query("address")
	tokenAddress := c.Query("tokenAddress")
	currencyType := c.Query("currencyType") //货币类型  韩元KRW 日元JPY 人民币CNY 美元USD
	if address == "" || currencyType == "" {
		response = global.Response{
			Code: http.StatusInternalServerError,
			Data: nil,
			Msg:  "address is empty",
		}
		c.JSON(http.StatusInternalServerError, response)
		return
	}

	rate := "1"

	if currencyType != "USD" {
		url := "https://gushitong.baidu.com/opendata?openapi=1&dspName=iphone&tn=tangram&client=app&query=USD" + currencyType + "&code=&word=&resource_id=5343&ma_ver=4&finClientType=pc"
		body, err := tools.SendGet(url)
		if err != nil {
			response = global.Response{
				Code: http.StatusInternalServerError,
				Data: nil,
				Msg:  err.Error(),
			}
			c.JSON(http.StatusInternalServerError, response)
			return
		}
		var data Response
		_ = json.Unmarshal(body, &data)
		rate = data.Result[0].DisplayData.ResultData.TplData.Result.MinuteData.Cur.Price
	}
	configData, err := models.GetConfigData(strings.ToUpper(chainName), chainId, "DAPP")
	if err != nil {
		response = global.Response{
			Code: http.StatusInternalServerError,
			Data: nil,
			Msg:  err.Error(),
		}
		c.JSON(http.StatusInternalServerError, response)
		return
	}

	// 创建结构体
	type BalanceInfo struct {
		Token string `json:"token"`
		Price string `json:"price"`
		Rate  string `json:"rate"`
	}

	var balanceList []BalanceInfo

	// 通过rpc地址获取公链币余额
	client, err := ethclient.Dial(configData.RpcUrl)
	if err != nil {
		response = global.Response{
			Code: http.StatusInternalServerError,
			Data: nil,
			Msg:  err.Error(),
		}
		c.JSON(http.StatusInternalServerError, response)
		return
	}
	defer client.Close()

	tokenInfo := models.GetMainToken(strconv.FormatInt(configData.UdunId, 10))
	mainPrice := getCurrentPrice(client, configData.MainAddress, configData, tokenInfo.Decimals)

	if mainPrice == "0" {
		mainPrice = "-1"
	}
	balanceList = append(balanceList, BalanceInfo{
		Token: strconv.FormatInt(configData.ChainID, 10),
		Price: mainPrice,
		Rate:  rate,
	})

	if tokenAddress != "" {
		tokenArr := strings.Split(tokenAddress, ",")

		for _, token := range tokenArr {
			contractAddress := common.HexToAddress(token)
			decimals, _ := contract.CallContractMethod(client, contractAddress, evmAbi.ERC20_ABI, "decimals")
			price := getCurrentPrice(client, token, configData, uint(decimals[0].(uint8)))
			//price := getCurrentPrice(client, token, configData, uint(18))

			balanceList = append(balanceList, BalanceInfo{
				Token: token,
				Price: price,
				Rate:  rate,
			})
		}
	}

	response = global.Response{
		Code: http.StatusOK,
		Data: balanceList,
		Msg:  "success",
	}

	c.JSON(http.StatusOK, response)

}

func getCurrentPrice(client *ethclient.Client, tokenAddress string, configData models.ScanConfig, decimal uint) string {
	if common.HexToAddress(tokenAddress).Hex() == common.HexToAddress(configData.UsdtAddress).Hex() {
		return "1"
	}
	routerABI, err := abi.JSON(strings.NewReader(evmAbi.UNISWAP_V2_ROUTER))
	if err != nil {
		log.Println("UNISWAP_V2_ROUTER解析失败", err)
		return "0"
	}

	// 使用 big.Int 来设置代币数量，避免浮点数精度问题
	amountIn := new(big.Int).Mul(big.NewInt(1), new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(decimal)), nil))

	path := []common.Address{
		common.HexToAddress(tokenAddress),
		common.HexToAddress(configData.UsdtAddress),
	}

	// 创建调用参数
	callData, err := routerABI.Pack("getAmountsOut", amountIn, path)
	if err != nil {
		log.Println("routerABI.Pack error:", err)
		return "0"
	}

	routerAddress := common.HexToAddress(configData.RouterAddress)
	msg := ethereum.CallMsg{
		To:   &routerAddress,
		Data: callData,
	}

	// 调用合约方法
	result, err := client.CallContract(context.Background(), msg, nil)
	if err != nil {
		log.Println("CallContract error:", err)
		return "0"
	}

	// 解析返回结果
	var amounts []*big.Int
	err = routerABI.UnpackIntoInterface(&amounts, "getAmountsOut", result)
	if err != nil {
		log.Println("UnpackIntoInterface error:", err)
		return "0"
	}

	// 获取当前价格
	if len(amounts) > 1 {
		//根据换出来的数字 再除以usdt的精度 得到价格
		usdtDecimal := big.NewInt(1000000000000000000)
		//如果是eth链 usdt的精度是6
		if configData.ChainID == 1 {
			usdtDecimal = big.NewInt(1000000)
		}

		price := new(big.Float).Quo(new(big.Float).SetInt(amounts[1]), new(big.Float).SetInt(usdtDecimal))
		priceStr := price.Text('f', int(decimal))
		return priceStr
	} else {
		return "0"
	}
}
